/*
 * rulebase.cpp
 *
 *  Created on: March 19, 2011
 *      Author: Mo
 */

 /* NOTE: ON MAY 30, I REMOVED THE LINE this->updateMostGainArray(); IN THE CONSTRUCTOR 
    RuleBase2B::RuleBase2B (ImageMatrix& matrixOld, ImageMatrix& matrixNew) DUE TO A BUG. THIS
	SHOULD BE FIXED LATER.
 */

#include "freefuncs.h"
#include <math.h>
#include<iostream>
#include "rulebase2b.h"
#include <assert.h>
#include "fixedtree.h"
#include "neighborhood2.h"
#include <algorithm>

LongInstance2B RuleBase2B::getInstance (int index) const
{
	return this->instance[index];
}

int RuleBase2B::getLabelOfIndex (int index) const
{
	return this->instance[index].getLabel();
}

void RuleBase2B::addInstanceUpdateMGA (const LongInstance2B& toBeAdded)
{
	this->addInstanceNOUpdateMGA(toBeAdded);
	//this->updateMostGainArray();
}


void RuleBase2B::addInstanceNOUpdateMGA(const LongInstance2B& toBeAdded)
{
	if (this->instance.size() > 0)
		//assert(this->getInstanceSize() == toBeAdded.getFeatureSize() && "You are adding an instance of different size");
		if (this->getInstanceSize() != toBeAdded.getFeatureSize())
			cout << "Shit happened" << endl;
	int copyIndex = -1; //the index of the identical copy of the instance in the rb. -1 means such copy does not exist.
	int conflictIndex = -1;//the index of the conflicting copy of the instance in the rb.
	//int tbaConflictNum = 0;//number of conflicting rules with toBeAdded instance already in the rb.
	int i = 0;

	while (i < (int) this->instance.size() && ( copyIndex == -1 || conflictIndex == -1))
	//while (i < (int) this->instance.size() && copyIndex == -1)
	{
		//if (this->instance[i].getTotalHash() == toBeAdded.getTotalHash())
			if (this->instance[i].hasSameFeature(toBeAdded))
				if (this->instance[i].getLabel() != toBeAdded.getLabel()) //we have a conflict here, but the copy of tba might be not in the rulebase though.
				{
					conflictIndex = i;
				}
				else
					copyIndex = i; //now we found the copy, the loop will break.
		i++;
	}
	if (copyIndex == -1) 
		if (conflictIndex == -1) //if we haven't found the copy (either identical or conflicting), the instance needs to be added.
		{
			//toBeAdded.setConflictNum(0);
			this->instance.push_back(toBeAdded);
			this->instance.back().setConflictNum(0);
		}
		else //There has been only a conflicting version. We update the conflicting instance and ALSO ADD THIS INSTANCE
		{
			this->instance[conflictIndex].incrementConflictNum();
			this->instance.push_back(toBeAdded);
			this->instance.back().setConflictNum(this->instance[conflictIndex].getOccuranceNum());
			this->conflictNum++;
		}
	else //there has been a copy before
		if (conflictIndex == -1) //Only an identical copy was found, no conflicting.
		{
			this->instance[copyIndex].addOthersCoordinates(toBeAdded);
		}
		else //both identical AND conflicting instances were found in the rulebase.
		{
			//instance[copyIndex] already has the conflictNum, does not need to be changed. this one is not conflicting.
			this->instance[copyIndex].addOthersCoordinates(toBeAdded);
			//instance[conflictIndex] already has the conflictNum so far, just needs an increment.
			this->instance[conflictIndex].incrementConflictNum();
			this->conflictNum++;
		}
		//this->separabilityIsValid = false;
}

//double RuleBase2B::updateSeparability (void)
//{
//	FixedTree ft (*this);
//	this->separability = (double) ft.getEmptyNodeNum() / (double) ft.getNodeNum();
//	this->separabilityIsValid = true;
//	return this->separability;
//}

int RuleBase2B::getInstanceNum() const
{
	return this->instance.size();
}

int RuleBase2B::getInstanceSize() const
{
	if (this->instance.size() == 0)
		return 0;
	else
		return this->ruleSize;
}

int RuleBase2B::getTheSizeOfOneNeighborhood () const
{
	if ((int)instance.size() == 0)
		//return -1;
		return 0;
	return instance[0].getNeighborhood2Size();
}

int RuleBase2B::getNBNumInOneInstance(void) const
{
	if (instance.size() == 0) 
		return 0;
	return instance[0].getNBNum();
}

void RuleBase2B::setInstanceSize(int size)
{
	this->ruleSize = size;
	//this->separabilityIsValid = false;
}

int RuleBase2B::getFeature(int inst, int feature) const
{
	return this->instance[inst].getFeature(feature);
}

int RuleBase2B::getLabel (int inst) const
{
	return this->instance[inst].getLabel();
}

void RuleBase2B::print(void) const
{
	cout << "The content of the RuleBase2B object:" << endl;
	for (int i = 0; i != (int) this->instance.size(); i++)
	{
		cout << i << ". ";
		this->instance[i].print();
	}
	cout << "Size of the ruleBase object: " << this->instance.size() << endl;
	cout << "Total conflicting instances: " << this->conflictNum << endl;
	cout << "Total number of non-unique instances in the whole rule base: " << this->repeatationNum << endl;
}

void RuleBase2B::printDetail(void) const
{
	cout << "The content of the RuleBase2B object:" << endl;
	for (int i = 0; i != (int) this->instance.size(); i++)
	{
		cout << i << ". ";
		this->instance[i].print();
		cout << "There are " << this->instance[i].getOccuranceNum() << " copies of this instance and "
			<< this->instance[i].getConflictNum() << " instances conflict this." << endl;
	}
	
	cout << "Size of the ruleBase object: " << this->instance.size() << endl;
	cout << "Total conflicting instances: " << this->conflictNum << endl;
	cout << "Total number of non-unique instances in the whole rule base: " << this->repeatationNum << endl;
}

void RuleBase2B::updateDataMembers4Resolved (void)
{
	this->conflictNum = 0;
	this->repeatationNum = 0;
	for (int i = 0; i != (int)this->instance.size(); i++)
	{
		if (this->instance[i].getConflictNum() > 0) //both the instance and its conflicting instance are conflicting!
			this->conflictNum += (this->instance[i].getConflictNum() + this->instance[i].getOccuranceNum());
		if (this->instance[i].getOccuranceNum() > 1)
			this->repeatationNum += this->instance[i].getOccuranceNum();
	}
	//if (!this->separabilityIsValid)
	//	this->updateSeparability();
}

double RuleBase2B::totalInformationWithRepeatation(void) const
{
	if (this->instance.size() < 2 || this->ruleSize < 2 || this->allSameClass())
		return 0;
	double result = 0;
	int totalInstances = 0; //This is different from instance.size(), because each instance might be occurred more than once.
	int instancesOfState[CELLSTATES];	//instancesOfState[i] tells how many instances with the label equal to i we have.
	double probability[CELLSTATES];
	for (int i = 0; i != CELLSTATES; i++)
	{
		instancesOfState[i] = 0;
		probability[i] = 0.0;
	}

	//counting number of repetition for each label
	for (int i = 0; i != (int)this->instance.size(); i++)
	{
		instancesOfState[this->instance[i].getLabel()] += this->instance[i].getOccuranceNum();
		totalInstances += this->instance[i].getOccuranceNum();
	}

	for (int i = 0; i != CELLSTATES; i++)
		if (instancesOfState[i] != 0)
		{
			probability[i] = instancesOfState[i]/(double)totalInstances;
			result -= (probability[i]) * (log(probability[i]) / log(2.0));
		}
	return result;
}

double RuleBase2B::totalInformationNORepeatation(void) const
{
	if (this->instance.size() < 2 || this->ruleSize < 2 || this->allSameClass())
		return 0;
	double result = 0;
	int instancesOfState[CELLSTATES];	//instancesOfState[i] tells how many instances with the label equal to i we have.
	double probability[CELLSTATES];
	for (int i = 0; i != CELLSTATES; i++)
	{
		instancesOfState[i] = 0;
		probability[i] = 0.0;
	}

	//counting number of repetition for each label
	for (int i = 0; i != (int)this->instance.size(); i++)
		instancesOfState[this->instance[i].getLabel()] ++;
		
	for (int i = 0; i != CELLSTATES; i++)
		if (instancesOfState[i] != 0)
		{
			probability[i] = instancesOfState[i]/(double)this->instance.size();
			result -= (probability[i]) * (log(probability[i]) / log(2.0));
		}
	return result;
}

//double RuleBase2B::gain(int featureIndex) const
//{
//	double result = 0;
//	if (this->instance.size() < 2)
//		return 0;
//	//RuleBase2B subRB [CELLSTATES]; //subRB[i] is a rulebase in which all the instances have feature[featureIndex] = i
//
//	RuleBase2B** subRB;
//	subRB = new RuleBase2B*[CELLSTATES];
//	for (int i = 0; i != CELLSTATES; i++)
//		subRB[i] = new RuleBase2B(this->ruleSize);
//	for (int i = 0; i != (int) this->instance.size(); i++)
//		subRB[this->instance[i].getFeature(featureIndex)]->addInstanceNOUpdateMGA(LongInstance2B(this->instance[i]));
//
//	for (int i = 0; i != CELLSTATES; i++)
//	{
//		result += (subRB[i]->instance.size() / 
//			(double) this->instance.size()) * subRB[i]->totalInformationNORepeatation();
//	}
//	
//	for (int i = 0; i != CELLSTATES; i++)
//		delete subRB[i];
//	delete[] subRB;
//	return (this->totalInformationNORepeatation() - result);
//}

bool RuleBase2B::allSameClass(void) const
{
	if (this->instance.size() == 0)
		return true;
	else
	{
		int i = 1;
		int expectedLabel = this->instance[0].getLabel();
		while (i < (int) this->instance.size() && this->instance[i].getLabel() == expectedLabel)
			i++;
		if (i == (int) this->instance.size())
			return true;
		else
			return false;
	}
}

//int RuleBase2B::featureWithMostGain() const
//{
//	return this->mostGainArray[0];
//}


//void RuleBase2B::updateMostGainArray(void)
//{
//	int ruleSize = this->getInstanceSize();
//	if (this->mostGainArray.size() == 0)
//		this->mostGainArray.resize(ruleSize);
//	
//	if (this->getInstanceNum() < 2)
//	{
//		for (int i = 0; i != this->ruleSize; i++)
//			this->mostGainArray[i] = i;
//		return;
//	}
//	int i = 0, j = 0;
//	double* gainArray = new double [ruleSize];
//	double* sortedValues = new double [ruleSize];
//	for (i = 0; i != ruleSize; i++)
//	{
//		gainArray[i] = this->gain(i);
//		sortedValues[i] = gainArray[i];
//	}
//
//	insertionSort<double>(sortedValues, ruleSize);
//
//	for (i = 0; i != this->ruleSize; i++)
//	{
//		while (gainArray[j] != sortedValues[i] && j < ruleSize)
//			j++;
//		this->mostGainArray[i] = j;
//		gainArray[j] = -1;
//		j = 0;
//	}
//	delete[] gainArray;
//	delete[] sortedValues;
//}
//
//void RuleBase2B::copyMostGainArray(int* targetArray, int mGASize)
//{
//	for (int i = 0; i != mGASize; i++)
//		targetArray[i] = this->mostGainArray[i];
//}

void RuleBase2B::resolve(void)
{
	for (int i = 0; i != (int) this->instance.size(); i++)
		for (int j = i+1; j != (int) this->instance.size(); j++)
			if (this->instance[i].getTotalHash() == this->instance[j].getTotalHash())
				//if (this->instance[i].hasSamePartialHash(this->instance[j])) // we have either a copy or a conflict here.
					if (this->instance[i].getLabel() == this->instance[j].getLabel()) //compress them to one, if they are the same
						std::cout << "Warning: rulebase::resolve() was called on an un-compressed rulebase object. Call rulebase::compress() first." << std::endl;
					else // set the conflictNum for each, if they are conflicting
						if (this->instance[i].getOccuranceNum() < this->instance[j].getOccuranceNum())
							this->instance.erase(this->instance.begin() + i);
						else
							this->instance.erase(this->instance.begin() + j);
	//this->updateMostGainArray();
	//this->separabilityIsValid = false;
}

void RuleBase2B::resolve2(void)
{
	bool conflictFound;
	int i, j;
	for ( i = 0; i != (int) this->instance.size(); i++)
	{
		conflictFound = false;
		j = i+1;
		while ( j < (int) this->instance.size() && !conflictFound )
		{
			if (this->instance[i].getTotalHash() == this->instance[j].getTotalHash()) //they might be the same
				//if (this->instance[i].hasSamePartialHash(this->instance[j])) //they are the same
				if (this->instance[i].hasSameFeature(this->instance[j]))
				{
					conflictFound = true;
					if (this->instance[i].getLabel() == this->instance[j].getLabel()) //compress them to one, if they are the same
						std::cout << "Warning: rulebase::resolve() was called on an un-compressed rulebase object. Call rulebase::compress() first." << std::endl;
					else // keep the most repeating one
						if (this->instance[i].getOccuranceNum() < this->instance[j].getOccuranceNum())
						{
							std::swap(this->instance[i], this->instance[j]);
							this->instance.erase(this->instance.begin() + j);
						}
						else
							this->instance.erase(this->instance.begin() + j);
				}
			j++;
		}
	}
	//this->updateMostGainArray();
	//this->separabilityIsValid = false;
}

void RuleBase2B::resolve3(void)
{
	bool conflictFound;
	vector<LongInstance2B>::iterator sml, lrg; //small and large, small is always smaller than large.
	for ( sml = this->instance.begin(); sml != this->instance.end(); sml++)
	{
		conflictFound = false;
		lrg = sml + 1;
		while ( lrg != this->instance.end() && !conflictFound )
		{
			if (/*sml->getTotalHash() == lrg->getTotalHash() &&*/ sml->hasSameFeature(*lrg)) //sml and lrg are pointing to the same features.
			{
				conflictFound = true;
				if (sml->getLabel() == lrg->getLabel()) //compress them to one, if they are the same
					std::cout << "Warning: rulebase::resolve() was called on an un-compressed rulebase object. Call rulebase::compress() first." << std::endl;
				else // keep the most repeating one
					if (sml->getOccuranceNum() < lrg->getOccuranceNum())
					{
						std::swap(*sml, *lrg);
						this->instance.erase(lrg);
					}
					else
						this->instance.erase(lrg);
				break; // TO BE REMOVED LATER. SHOULD BE WORKING WITHOUT BREAK.
			}
			else
				lrg++;
		}
	}
	//this->updateMostGainArray();
	//this->separabilityIsValid = false;
}

RuleBase2B::RuleBase2B()
{
	this->conflictNum = 0;
	this->ruleSize = 0;
	this->repeatationNum = 0;
	//this->separability = 0;
	//this->separabilityIsValid = false;
}

void RuleBase2B::copySizesFrom(const RuleBase2B& other)
{
	this->ruleSize = other.getInstanceSize();
	//this->mostGainArray.resize(this->ruleSize);
	//for (int i = 0; i != this->ruleSize; i++)
	//	this->mostGainArray[i] = -1;
	//this->separabilityIsValid = false;
}

void RuleBase2B::setDynamicSizes(int size)
{
	this->ruleSize = size;
	//this->mostGainArray.resize(this->ruleSize);
	//for (int i = 0; i != this->ruleSize; i++)
	//	this->mostGainArray[i] = -1;
	//this->separabilityIsValid = false;
}

int RuleBase2B::match (const LongInstance2B& sampleRule) const
{
	for (int i = 0; i != (int) this->instance.size(); i++)
	{
		if (sampleRule.hasSameFeature(this->instance[i]))
			return i;
	}
	return -1;
}

void RuleBase2B::makeNextImagePhase (const ImageMatrix& source, ImageMatrix& target, int nbRadius) const
{
	assert(this->ruleSize == intPosPow((2*nbRadius+1),2) && source.getColNum() == target.getColNum()
		&& source.getRowNum() == target.getRowNum() && this->conflictNum == 0);
	Neighborhood2 neighborhood;
	LongInstance2B tempLI;
	int ruleIndex;
	int color = 0;
	for (int row = 0; row!= source.getRowNum(); row++)
		for (int col = 0; col != source.getColNum(); col++)
		{
			//neighborhood = new Neighborhood2;
			neighborhood.updateFromImageMatrix(source, row, col, nbRadius);
			tempLI.clear();
			tempLI.addPhase(neighborhood, 0);
			ruleIndex = this->match(tempLI);
			if (ruleIndex != -1)
			{
				color = this->getLabel(ruleIndex);
				target.setData_noClusterUpdate(row, col, color);
			}
		}
}

RuleBase2B::RuleBase2B(int ruleSize)
{
	this->conflictNum = 0;
	this->ruleSize = ruleSize;
	this->repeatationNum = 0;
	//this->separability = 0;
	//this->mostGainArray.resize(ruleSize);
	//this->separabilityIsValid = false;
}

RuleBase2B::RuleBase2B (const RuleBase2B& other)
{
	this->conflictNum = other.conflictNum;
	this->repeatationNum = other.repeatationNum;
	//this->separability = other.separability;
	this->ruleSize = other.getInstanceSize();
	//this->mostGainArray.resize(this->ruleSize);
	for (int i = 0; i != (int)other.getInstanceNum(); i++)
		this->instance.push_back(other.instance[i]);
	//for (int i = 0; i != this->ruleSize; i++)
	//	this->mostGainArray[i] = other.mostGainArray[i];
	//this->separabilityIsValid = other.separabilityIsValid;
}

RuleBase2B::RuleBase2B (ImageMatrix& matrixOld, ImageMatrix& matrixNew, int nbRadius)
{
	assert (matrixOld.getRowNum() == matrixNew.getRowNum() && matrixOld.getColNum() == matrixNew.getColNum() && 
		"Warning: the matrices have different sizes in RuleBase2B::RuleBase2B(ImageMatrix, ImageMatrix)");
	this->conflictNum = 0;
	this->repeatationNum = 0;
	//this->separability = 0;
	//this->separabilityIsValid = false;
	this->ruleSize = intPosPow((2*nbRadius+1),2);
	Neighborhood2 nb;
	LongInstance2B toBeAdded;
	for (int row = 0; row != matrixOld.getRowNum(); row++)
		for (int col = 0; col != matrixOld.getColNum(); col++)
		{
			//nb = new Neighborhood2;
			nb.updateFromImageMatrix(matrixOld, row, col, nbRadius);
			toBeAdded.addPhase(nb, matrixNew.getValue(row, col));
			toBeAdded.addCoordinate(row, col);
			this->addInstanceNOUpdateMGA(toBeAdded);
			toBeAdded.clear();
		}
	
	//this->updateMostGainArray(); //REMOVED ON MAY 30
	//this->separabilityIsValid = false;
}

RuleBase2B::RuleBase2B (const ImageMatrix* images, int imageNum, int nbRadius) : conflictNum(0), repeatationNum(0)
{
#ifdef COMMENTS_ON
	cout << "\nCreating RuleBase2B from an array of ImageMatrix objects.\n";
#endif
	//Size of the IF part in the rules.
	assert (imageNum > 0 && "Error: not enough images to make the RuleBase2B.");
	this->ruleSize = intPosPow((2*nbRadius+1),2) * (imageNum-1); //the last image is left for the labels, the rest make the IF part.

	// a neighborhood to be filled with the current neighborhood of each SINGLE imagematrix (not 8 of them) each time.
	Neighborhood2 nb; 

	//The longInstance created by concatenating the neighborhoods of the pixels in 'imageNum-1' number of images in the 
	// IF part and the value of the pixels in the last imagematrix. 
	LongInstance2B toBeAdded;  

	for (int row = 0; row != MROWS; row++)
	{
#ifdef COMMENTS_ON
		cout << ".";
#endif
		for (int col = 0; col != MCOLS; col++)
		{
			for (int n = 0; n < imageNum-1; n++) //for each pixel(row,col), create the history of the neighborhoods
			{
				//nb = new Neighborhood2;
				nb.updateFromImageMatrix(images[n], row, col, nbRadius);
				toBeAdded.addPhase(nb, images[n+1].getValue(row, col)); //this also updates the label at each time.
			}
			toBeAdded.addCoordinate(row, col);
			this->addInstanceNOUpdateMGA(toBeAdded);
			toBeAdded.clear();
		}
	}
	////WE DO NOT NEED TO RESOLVE THE RULEBASE ANYMORE. RECTANGLES ARE CREATED FROM PAIR OF CONFLICTING RULES.
	//cout << "Non-compressed RuleBase2B created, not resolving the conflicts.\n";
	//this->resolve3();	
	//this->updateMostGainArray(); //REMOVED ON MAY 30
	//this->separabilityIsValid = false;
#ifdef COMMENTS_ON
	cout << endl;
#endif
}

RuleBase2B::RuleBase2B (const EightLayerIM& eim, int nbRadius)
{
#ifdef COMMENTS_ON
	cout << "\nCreating RuleBase2B from an EightLayerIM object).\n";
#endif
	//Size of the IF part in the rules.
	int imageNum = eim.getValidImages();
	this->ruleSize = intPosPow((2*nbRadius+1),2) * (imageNum-1); //the last image is left for the labels, the rest make the IF part.

	// a neighborhood to be filled with the current neighborhood of each SINGLE imagematrix (not 8 of them) each time.
	Neighborhood2 nb; 

	//The longInstance created by concatenating the neighborhoods of the pixels in 'imageNum-1' number of images in the 
	// IF part and the value of the pixels in the last imagematrix. 
	LongInstance2B toBeAdded;  

	for (int row = 0; row != MROWS; row++)
	{
#ifdef COMMENTS_ON
		cout << ".";
#endif
		for (int col = 0; col != MCOLS; col++)
		{
			for (int n = 0; n < imageNum-1; n++) //for each pixel(row,col), create the history of the neighborhoods
			{
				//nb = new Neighborhood2;
				nb.updateFromImageMatrix(eim.getImage(n), row, col, nbRadius);
				toBeAdded.addPhase(nb, eim.getImage(n+1).getValue(row, col)); //this also updates the label at each time.
			}
			toBeAdded.addCoordinate(row, col);
			this->addInstanceNOUpdateMGA(toBeAdded);
			toBeAdded.clear();
		}
	}
	////WE DO NOT NEED TO RESOLVE THE RULEBASE ANYMORE. RECTANGLES ARE CREATED FROM PAIR OF CONFLICTING RULES.
	//cout << "Non-compressed RuleBase2B created, not resolving the conflicts.\n";
	//this->resolve3();	
	//this->updateMostGainArray(); //REMOVED ON MAY 30
	//this->separabilityIsValid = false;
#ifdef COMMENTS_ON
	cout << endl;
#endif
}


RuleBase2B::RuleBase2B (const EightLayerIM& eim, int start, int end, int nbRadius)
{
	assert (start < eim.getValidImages() && start >= 0 && end > start && end <= eim.getValidImages() 
		&& "Error: invalid start and end indices for creating the rule base");
#ifdef COMMENTS_ON
	cout << "\nCreating RuleBase2B from [part of] an EightLayerIM object).\n";
#endif
	//Size of the IF part in the rules.
	//int imageNum = end-start;
	this->ruleSize = intPosPow((2*nbRadius+1),2) * (end-start); //the last image is left for the labels, the rest make the IF part.

	// a neighborhood to be filled with the current neighborhood of each SINGLE imagematrix (not 8 of them) each time.
	Neighborhood2 nb; 

	//The longInstance created by concatenating the neighborhoods of the pixels in 'imageNum-1' number of images in the 
	// IF part and the value of the pixels in the last imagematrix. 
	LongInstance2B toBeAdded;  

	for (int row = 0; row != MROWS; row++)
	{
#ifdef COMMENTS_ON
		cout << ".";
#endif
		for (int col = 0; col != MCOLS; col++)
		{
			for (int n = start; n < end; n++) //for each pixel(row,col), create the history of the neighborhoods
			{
				//nb = new Neighborhood2(nbRadius);
				nb.updateFromImageMatrix(eim.getImage(n), row, col, nbRadius);
				toBeAdded.addPhase(nb, eim.getImage(n+1).getValue(row, col)); //this also updates the label at each time.
			}
			toBeAdded.addCoordinate(row, col);
			this->addInstanceNOUpdateMGA(toBeAdded);
			toBeAdded.clear();
		}
	}
	////WE DO NOT NEED TO RESOLVE THE RULEBASE ANYMORE. RECTANGLES ARE CREATED FROM PAIR OF CONFLICTING RULES.
	//cout << "Non-compressed RuleBase2B created, not resolving the conflicts.\n";
	//this->resolve3();	
	//this->updateMostGainArray(); //REMOVED ON MAY 30
	//this->separabilityIsValid = false;
	updateConflictNum(); //TODO: is it neede??
#ifdef COMMENTS_ON
	cout << endl;
#endif
}


RuleBase2B::~RuleBase2B ()
{
	this->instance.clear();
	this->conflictNum = 0;
	this->repeatationNum = 0;
	//this->separability = 0;
}


int RuleBase2B::indexOfConflicting (const LongInstance2B& myRule) const
{
	for (int i = 0; i < (int)this->instance.size(); i++)
		if (this->instance[i].hasSameFeature(myRule) && this->instance[i].getLabel() != myRule.getLabel())
			return i;
	return -1;
}

int RuleBase2B::totalConflicts() const
{
	int result = 0;
	for (int i = 0; i < (int) this->instance.size(); i++)
		if (indexOfConflicting(instance[i]) != -1)
			result++;
	return result/2;
}

//int RuleBase2B::totalConflicts() const //THIS IS FOR LOGGING/DEBUGGING
//{
//	int result = 0;
//	//cout << endl << "Conflicting indices: ";
//	string fileName("txt\\RB2BDebug\\conflict_for_" + int2Str(this->ruleSize) + ".txt");
//	ofstream file(fileName);
//	file << this->ruleSize << " is the size of the rules.\nConflicts:\n";
//	for (int i = 0; i < (int) this->instance.size(); i++)
//	{
//		if (indexOfConflicting(instance[i]) != -1)
//		{
//			result++;
//			file << result << "- \n";
//			file << i << ": " << this->instance[i] << "\t at: ";
//			for (int j = 0; j < instance[i].getCooNum(); ++j)
//				file << instance[i].getCoordinate(j) << ", ";
//			file << endl;
//			file << indexOfConflicting(instance[i]) << ": " << this->instance[indexOfConflicting(instance[i])] << "\t at: ";
//			for (int j = 0; j < instance[indexOfConflicting(instance[i])].getCooNum(); ++j)
//				file << instance[indexOfConflicting(instance[i])].getCoordinate(j) << ", ";
//			file << endl;
//		}
//	}
//	file << "Total of " << result/2 << " conflicts.";
//	return result/2;
//}

int RuleBase2B::getNetInfoSizeInBits(ruleSizeCalcMethod method) const
{
	vector<LongInstance2B>::const_iterator i;
	int result = 0;
	switch (method)
	{
	case rulesAndCoos:
	for (i = instance.begin(); i != instance.end(); ++i)
		result += i->getNetInfoSizeInBits();
	return result;

	case onlyLI2s:
	for (i = instance.begin(); i != instance.end(); ++i)
		result += i->getFeatureSize() * BITS_PER_NBR; 
	return result;

	default:
	cout << "Error: this method is not implemented yet to calculate the getNetInfoSizeInBits(). Returning -1." << endl;
	return -1;
	}

}

//void RuleBase2B::deleteRulesWithZeroThenPart ()
//{
//	vector<LongInstance2B>::const_iterator i = instance.begin();
//	while (i != instance.end())
//	{
//		if (i->getLabel() == 0)
//			i = instance.erase(i);
//		else
//			++i;
//	}
//	instance.shrink_to_fit();
//}

void RuleBase2B::deleteRulesWithZeroThenPart ()
{
	instance.erase(remove_if(instance.begin(), instance.end(), li2bLabelIsZero), instance.end());
//	vector<LongInstance2B>::const_iterator i = instance.begin();
//	while (i != instance.end())
//	{
//		if (i->getLabel() == 0)
//			i = instance.erase(i);
//		else
//			++i;
//	}
//	instance.shrink_to_fit();
}

int RuleBase2B::getConflictNum() const
{
	return conflictNum;
}

//RuleBase2B& RuleBase2B::removeNeighbor_emptyCoo (int neighborIndex) const
//{
//	RuleBase2B* result = new RuleBase2B();
//	//result->conflictNum = conflictNum;
//	result->repeatationNum = repeatationNum;
//
//	result->ruleSize = getInstanceSize() - 1;
//	for (vector<LongInstance2B>::const_iterator lII = this->instance.begin(); lII != this->instance.end(); ++lII)
//		result->instance.push_back(lII->removeFeature_emptyCoo(neighborIndex));
//	result->updateConflictNum();
//	return *result;
//}

int RuleBase2B::recalculateConflictNum() const
{
	int result = 0;
	if (hasRepeatingRule())
	{
		cout << "Error: The RuleBase2 object has a repeating rule. There has been a problem somewhere." << endl;
		assert(false);
	}
	int* ruleConflictNum = new int [instance.size()]; //this array holds the number of conflicting instances for each rule. Each element of this value should usually be 0 or 1.
	for (int i = 0; i < (int)instance.size(); ++i)
		ruleConflictNum[i] = -1;
	for (int i = 0; i < (int)instance.size(); ++i)
		if (ruleConflictNum[i] == -1)
		{
			int j = i+1;
			int conflictIndex = -1;
			while (j < (int)instance.size() && conflictIndex == -1)
			{
				if (instance[i].conflicts(instance[j]))
					conflictIndex = j;
				++j;
			}
			
			if (conflictIndex == -1)
				ruleConflictNum[i] = 0;
			else
			{
				ruleConflictNum[i] = 1;
				ruleConflictNum[conflictIndex] = 1;
			}
		}
	for (int i = 0; i < (int)instance.size(); ++i)
		result += ruleConflictNum[i];
	return result;
}

void RuleBase2B::updateConflictNum()
{
	conflictNum = recalculateConflictNum();
}

bool RuleBase2B::hasRepeatingRule() const
{
	for (int i = 0; i < (int)instance.size(); ++i)
		for (int j = i+1; j < (int)instance.size(); ++j)
			if (instance[i] == instance[j])
				return true;
	return false;
}

RuleBase2B::RuleBase2B(LongInstance2B* PLI2B, int ruleNum)
{
	for (int i = 0; i < ruleNum; ++i)
		this->instance.push_back(PLI2B[i]);
	this->ruleSize = PLI2B[0].getFeatureSize();
	this->updateConflictNum();
}

RuleBase2B& RuleBase2B::operator= (const RuleBase2B& other)
{
	if (&other == this)
		return *this;
	this->instance.clear();
	this->conflictNum = other.conflictNum;
	this->repeatationNum = other.repeatationNum;
	this->ruleSize = other.ruleSize;
	for (int i = 0; i < (int) other.instance.size(); ++i)
		instance.push_back(other.instance[i]);

	return *this;
}


//RuleBase2B::RuleBase2B(string fileName) : conflictNum(0), repeatationNum(0), ruleSize(0)
//{
//	ifstream file(fileName);
//	string ruleSizeTag("rule size:");
//	string ruleNumTag("rule num:");
//	int ruleSize;
//	int ruleNum;
//	stringstream tempLineSS;
//	string tempLineS;
//	getline(file,tempLineS);
//	if (tempLineS == ruleSizeTag)
//	{
//		getline(file,tempLineS);
//		//tempLineSS1 << tempLineS;
//		//tempLineSS1 >> ruleSize;
//		ruleSize = str2Int(tempLineS);
//	}
//	else
//		cerr << "Error: File " << fileName << " should start with the tag \"rule size:\"" << endl;
//	
//	getline(file,tempLineS);
//	if (tempLineS == ruleNumTag)
//	{
//		getline(file,tempLineS);
//		//tempLineSS << tempLineS;
//		//tempLineSS >> ruleNum;
//		ruleNum = str2Int(tempLineS);
//	}
//	else
//		cerr << "Error: File " << fileName << " should have the tag \"rule num:\" in the third line." << endl;
//	
//	this->ruleSize = ruleSize;
//	string ifPart;
//	int label;
//	for (int ruleIndex = 0; ruleIndex < ruleNum; ruleIndex++)
//	{
//		file >> ifPart;
//		file >> label;
//		LongInstance2B toBeAdded(ifPart, label);
//		//this->instance.push_back(toBeAdded);
//		this->addInstanceNOUpdateMGA(toBeAdded);
//	}
//	//this->resolve3();
//	//this->compress();
//	//this->updateConflictNum();
//}

int RuleBase2B::getRepeatationNum() const
{
	return repeatationNum;
}


RuleBase2B::RuleBase2B(string fileName) : conflictNum(0), repeatationNum(0), ruleSize(0)
{
	ifstream file(fileName.data());
	string nbSizeTag("neighborhood size:");
	string nbNumTag("neighborhoods per rule:");
	string ruleNumTag("rule num:");
	int nbSize;
	int nbNum;
	int ruleNum;
	stringstream tempLineSS;
	string tempLineS;
	getline(file,tempLineS);
	if (tempLineS == nbSizeTag)
	{
		getline(file,tempLineS);
		nbSize = str2Int(tempLineS);
	}
	else
		cerr << "Error: File " << fileName << " should start with the tag \"" + nbSizeTag + "\"" << endl;
	
	getline(file,tempLineS);
	if (tempLineS == nbNumTag)
	{
		getline(file,tempLineS);
		nbNum = str2Int(tempLineS);
	}
	else
		cerr << "Error: File " << fileName << " should have the tag \"" + nbNumTag + " in the third line." << endl;

	getline(file,tempLineS);
	if (tempLineS == ruleNumTag)
	{
		getline(file,tempLineS);
		ruleNum = str2Int(tempLineS);
	}
	else
		cerr << "Error: File " << fileName << " should have the tag \"" + ruleNumTag + "\" in the fifth line." << endl;
	
	this->ruleSize = nbNum * nbSize;
	string* nbStr = new string[nbNum];
	//for (int nbIndex = 0; nbIndex < nbNum; ++nbIndex)
	//	nbStr[nbIndex] = "";
	int label;
	for (int ruleIndex = 0; ruleIndex < ruleNum; ruleIndex++)
	{
		for (int nbIndex = 0; nbIndex < nbNum; ++nbIndex)
			file >> nbStr[nbIndex];
		
		file >> label;
		LongInstance2B toBeAdded(nbStr, nbNum, label);
		this->addInstanceNOUpdateMGA(toBeAdded);
	}

}
ostream& operator<< (ostream& stream, const RuleBase2B& rb2b)
{
	for (int i = 0; i != (int) rb2b.instance.size(); i++)
	{
		stream << i << ". ";
		stream << rb2b.instance[i] << endl;
	}
	stream << "Size of the ruleBase object: " << rb2b.getInstanceNum() << endl;
	stream << "Length of each rule: " << rb2b.getInstanceSize() << endl;
	stream << "Size of each neighborhood in one rule: " << rb2b.getTheSizeOfOneNeighborhood() << endl;
	stream << "Total conflicting instances: " << rb2b.conflictNum << endl;
	return stream;
}